import { Controller } from './Controller';
import { WebBuilder } from './WebBuilder';
import { Method } from '../utils/Method';
import { compose, Composer } from '../utils/compose';
import { WebMiddleware, WebPlugin } from '../plugins/Plugin';

export class WebController<Props = {}, State = {}> extends Controller {
  public/*protected*/ builders: WebBuilder<Props, State>[] = [];

  public get(uri: string | string[]): WebBuilder<Props, State> {
    const builder = new WebBuilder(this.prefix, uri, [Method.get, Method.head]);
    this.builders.push(builder);
    return builder;
  }

  public post(uri: string | string[]): WebBuilder<Props, State> {
    const builder = new WebBuilder(this.prefix, uri, [Method.post]);
    this.builders.push(builder);
    return builder;
  }

  public put(uri: string | string[]): WebBuilder<Props, State> {
    const builder = new WebBuilder(this.prefix, uri, [Method.put]);
    this.builders.push(builder);
    return builder;
  }

  public patch(uri: string | string[]): WebBuilder<Props, State> {
    const builder = new WebBuilder(this.prefix, uri, [Method.patch]);
    this.builders.push(builder);
    return builder;
  }

  public delete(uri: string | string[]): WebBuilder<Props, State> {
    const builder = new WebBuilder(this.prefix, uri, [Method.delete]);
    this.builders.push(builder);
    return builder;
  }

  public all(uri: string | string[]): WebBuilder<Props, State> {
    const builder = new WebBuilder(this.prefix, uri, Object.values(Method));
    this.builders.push(builder);
    return builder;
  }

  public/*protected*/ createMiddleware(globalToLocal?: Composer): WebMiddleware {
    const groupPlugins = this.getManager().getLocalPlugins().getPlugins();
    const builders = this.builders;
    const buildersLength = builders.length;

    return (ctx, next) => {
      const { path, method } = ctx.request;
      const middleware: Array<WebPlugin | WebMiddleware> = [];

      for (let i = 0; i < buildersLength; ++i) {
        const builder = builders[i];
        const params = builder.matchAndGetParams(path, method);

        if (params) {
          middleware.push(
            (c, n) => {
              const { request } = c;

              // Reset dynamic data
              // @ts-expect-error
              request['query'] = request['body'] = request['params'] = undefined;
              request.rawParams = params;

              return n();
            },
            ...builder.getPlugins(),
          );
        }
      }

      if (!middleware.length) {
        return next();
      }

      middleware.unshift(...groupPlugins);

      if (globalToLocal) {
        middleware.unshift(globalToLocal);
      }

      return compose(middleware)(ctx, next);
    };
  }
}
